package net.qiqbframework.messaging.annotation;

import net.qiqbframework.common.sorting.AnnotatedPriorityComparator;

import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;


public class MultiExecutableHandlerEnhancer implements ExecutableHandlerWrapper {

    private final ExecutableHandlerWrapper[] enhancers;


    public static MultiExecutableHandlerEnhancer ordered(ExecutableHandlerWrapper... delegates) {
        return ordered(Arrays.asList(delegates));
    }


    public static MultiExecutableHandlerEnhancer ordered(Collection<ExecutableHandlerWrapper> delegates) {
        return new MultiExecutableHandlerEnhancer(flatten(delegates));
    }


    public MultiExecutableHandlerEnhancer(ExecutableHandlerWrapper... delegates) {
        this.enhancers = Arrays.copyOf(delegates, delegates.length);
    }

    /**
     * Initializes an instance that delegates to the given {@code delegates}, in the order provided. Changes in
     * the given List are not reflected in the created instance.
     *
     * @param delegates The list of enhancers providing the parameter values to use
     */
    public MultiExecutableHandlerEnhancer(Collection<ExecutableHandlerWrapper> delegates) {
        this.enhancers = delegates.toArray(new ExecutableHandlerWrapper[delegates.size()]);
    }

    private static ExecutableHandlerWrapper[] flatten(Collection<ExecutableHandlerWrapper> enhancers) {
        List<ExecutableHandlerWrapper> flattened = new ArrayList<>(enhancers.size());
        for (ExecutableHandlerWrapper handlerEnhancer : enhancers) {
            if (handlerEnhancer instanceof MultiExecutableHandlerEnhancer) {
                flattened.addAll(((MultiExecutableHandlerEnhancer) handlerEnhancer).getDelegates());
            } else {
                flattened.add(handlerEnhancer);
            }
        }
        flattened.sort(AnnotatedPriorityComparator.getInstance());
        return flattened.toArray(new ExecutableHandlerWrapper[flattened.size()]);
    }

    /**
     * Returns the delegates of this instance, in the order they are evaluated to resolve parameters.
     *
     * @return the delegates of this instance, in the order they are evaluated to resolve parameters
     */
    public List<ExecutableHandlerWrapper> getDelegates() {
        return Arrays.asList(enhancers);
    }

    @Override
    public MessageExecutableHandler wrap(@Nonnull MessageExecutableHandler original) {
        MessageExecutableHandler resolver = original;
        for (ExecutableHandlerWrapper enhancer : enhancers) {
            resolver = enhancer.wrap(resolver);
        }
        return resolver;
    }
}
